#include <interfaces/SystemSocket.h>
#include "../SystemEventMonitor.h"

namespace Greatewall
{
    namespace Network
    {
        SystemEventMonitor::SystemEventMonitor()
        {
            FD_ZERO(&_in_fd_set);
            FD_ZERO(&_out_fd_set);
            FD_ZERO(&_exp_fd_set);

            FD_ZERO(&_in);
            FD_ZERO(&_out);
            FD_ZERO(&_exp);

            _stop_monitor = false;
        }

        bool
        SystemEventMonitor::registerSocket(SystemSocket* sock, SocketEventType event_type)
        {
            std::pair<SystemSocketSet::iterator, bool> res = _system_socks.insert(sock);

            SystemSocketSet::iterator find = res.first;

            if (find == _system_socks.end())
            {
                // TODO: Error handling
                return false;
            }

            switch(event_type)
            {
                default:
                case NONE:
                {
                    FD_CLR(sock->hdl(), &_in_fd_set);
                    FD_CLR(sock->hdl(), &_out_fd_set);
                    break;
                }
                case IN:
                {
                    FD_SET(sock->hdl(), &_in_fd_set);
                    FD_CLR(sock->hdl(), &_out_fd_set);
                    break;
                }
                case OUT:
                {
                    FD_CLR(sock->hdl(), &_in_fd_set);
                    FD_SET(sock->hdl(), &_out_fd_set);
                    break;
                }
                case INOUT:
                {
                    FD_SET(sock->hdl(), &_in_fd_set);
                    FD_SET(sock->hdl(), &_out_fd_set);
                    break;
                }
            }
            return true;
        }

        bool
        SystemEventMonitor::unregisterSocket(SystemSocket* sock)
        {
            SystemSocketSet::iterator find = _system_socks.find(sock);

            if (find == _system_socks.end())
                return false;

            FD_CLR(sock->hdl(), &_in_fd_set);
            FD_CLR(sock->hdl(), &_out_fd_set);
            FD_CLR(sock->hdl(), &_exp_fd_set);

            FD_CLR(sock->hdl(), &_in);
            FD_CLR(sock->hdl(), &_out);
            FD_CLR(sock->hdl(), &_exp);

            _system_socks.erase(find);

            return true;
        }

        void
        SystemEventMonitor::run()
        {
            do
            {
                // TODO: add support for except set
                _in  = _in_fd_set;
                _out = _out_fd_set;
                _exp = _exp_fd_set;

                int nfds   = _system_socks.size() ? (**_system_socks.rbegin()).hdl() + 1 : 0;

                const timespec* expire = _timer_event_manager.getClosestTimeoutPeriod();

                int num =  pselect(nfds, &_in, &_out, &_exp, expire, NULL);

                if (num > 0)
                {
                    SystemSocketSet::iterator it = _system_socks.begin();
                    for (; it != _system_socks.end() && num > 0;)
                    {
                        SystemSocket* sock = *it;

                        // Get the next iterator, in case the current one is
                        // obsoleted.
                        it++;

                        SysSockHandler hdl = sock->hdl();
                        if (FD_ISSET(hdl, &_in))
                        {
                            sock->handleInbound();
                            num--;
                        }
                        if (FD_ISSET(hdl, &_out))
                        {
                            sock->handleOutbound();
                            num--;
                        }
                        if (FD_ISSET(hdl, &_exp))
                        {
                            num--;
                        }
                    }
                }
                _timer_event_manager.checkTimerEvents();
            }while(!_stop_monitor);
        }
    }
}
